#ifndef SQLITE_WRAPPER_H
#define SQLITE_WRAPPER_H

#include <sqlite3.h>
#include <string_view>
#include <string>
#include <cstdint>

/// @brief Error codes for database operations
enum class DbError {
    SUCCESS = 0,           ///< Operation completed successfully
    DATABASE_ERROR,        ///< General database error
    NOT_FOUND,             ///< Record not found
    CONSTRAINT_VIOLATION,  ///< Database constraint violation (e.g., unique constraint)
    INVALID_INPUT,         ///< Invalid input parameters
    CONNECTION_FAILED,     ///< Failed to connect to database
    PREPARE_FAILED,        ///< Failed to prepare SQL statement
    BIND_FAILED,           ///< Failed to bind parameters
    EXECUTION_FAILED,      ///< Failed to execute statement
    ALLOCATION_FAILED      ///< Memory allocation failed
};

/// @brief RAII wrapper for sqlite3 database connection
class DatabaseConnection {
public:
    /// @brief Construct a new Database Connection object
    /// @param db_path Path to the database file
    explicit DatabaseConnection(std::string_view db_path) noexcept;

    /// @brief Destroy the Database Connection object
    ~DatabaseConnection() noexcept;

    /// @brief Get the underlying sqlite3 pointer
    /// @return sqlite3* Pointer to the sqlite3 database connection
    sqlite3* get() const noexcept { return db_; }

    /// @brief Check if the connection is valid
    /// @return true if connection is valid, false otherwise
    bool is_valid() const noexcept { return db_ != nullptr; }

    /// @brief Get the error message for the last operation
    /// @return std::string Error message
    std::string get_error_message() const noexcept;

private:
    sqlite3* db_;
};

/// @brief RAII wrapper for sqlite3 prepared statement
class PreparedStatement {
public:
    /// @brief Construct a new Prepared Statement object
    /// @param db Database connection
    /// @param sql SQL statement to prepare
    PreparedStatement(DatabaseConnection& db, std::string_view sql) noexcept;

    /// @brief Destroy the Prepared Statement object
    ~PreparedStatement() noexcept;

    /// @brief Bind an integer value to a parameter
    /// @param index Parameter index (1-based)
    /// @param value Value to bind
    /// @return DbError SUCCESS on success, error code on failure
    [[nodiscard]] DbError bind_int(int index, int value) noexcept;

    /// @brief Bind a 64-bit integer value to a parameter
    /// @param index Parameter index (1-based)
    /// @param value Value to bind
    /// @return DbError SUCCESS on success, error code on failure
    [[nodiscard]] DbError bind_int64(int index, std::int64_t value) noexcept;

    /// @brief Bind a text value to a parameter
    /// @param index Parameter index (1-based)
    /// @param value Value to bind
    /// @return DbError SUCCESS on success, error code on failure
    [[nodiscard]] DbError bind_text(int index, std::string_view value) noexcept;

    /// @brief Execute the prepared statement
    /// @return DbError SUCCESS on success, error code on failure
    [[nodiscard]] DbError step() noexcept;

    /// @brief Get the number of rows affected by the last INSERT, UPDATE, or DELETE statement
    /// @return int Number of rows affected
    int changes() const noexcept;

    /// @brief Get the id of the last inserted row
    /// @return std::int64_t Row id
    std::int64_t last_insert_rowid() const noexcept;

    /// @brief Get column count in the result set
    /// @return int Number of columns
    int column_count() const noexcept;

    /// @brief Get integer value from a column
    /// @param index Column index (0-based)
    /// @return int Column value
    int column_int(int index) const noexcept;

    /// @brief Get 64-bit integer value from a column
    /// @param index Column index (0-based)
    /// @return std::int64_t Column value
    std::int64_t column_int64(int index) const noexcept;

    /// @brief Get text value from a column
    /// @param index Column index (0-based)
    /// @return std::string_view Column value
    std::string_view column_text(int index) const noexcept;

    /// @brief Reset the prepared statement for reuse
    /// @return DbError SUCCESS on success, error code on failure
    [[nodiscard]] DbError reset() noexcept;

    /// @brief Check if there are more rows in the result set
    /// @return true if there are more rows, false otherwise
    bool has_row() const noexcept { return has_row_; }

private:
    sqlite3_stmt* stmt_;
    bool has_row_;
};

#endif // SQLITE_WRAPPER_H